/*
 * $Id: Launcher.java 1259 2007-01-09 14:23:47Z tcoupaye $
 *
 * Behavior Protocols extensions for static and runtime checking
 * developed for the Julia implementation of Fractal.
 *
 * Copyright (C) 2006
 *    Formal Methods In Software Engineering Group
 *    Institute of Computer Science
 *    Academy of Sciences of the Czech Republic
 *
 * Copyright (C) 2006 France Telecom
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * Contact: ft@nenya.ms.mff.cuni.cz
 * Authors: Tomas Bures <bures@nenya.ms.mff.cuni.cz>
 *
 */

package org.objectweb.fractal.bpc.adl;

import java.util.HashMap;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.LifeCycleController;

import org.objectweb.fractal.adl.Factory;
import org.objectweb.fractal.adl.FactoryFactory;


import org.objectweb.fractal.bpc.jpf.JPFChecker;
import org.objectweb.fractal.bpc.staticchecker.NestedCheckingResult;
import org.objectweb.fractal.bpc.staticchecker.ProtocolChecker;

import org.objectweb.fractal.util.Fractal;


/**
 * A class for launching an application described by an ADL extended of protocols.
 *
 * <p>Usage:
 * <p><code>Launcher [-check | -checkjpf &lt;tmp directory&gt;] &lt;definition&gt; [ &lt;itf&gt; ]</code>
 * <p>where <code>&lt;definition&gt;</code> is the name of the component to be instantiated 
 * and started, and <code>&lt;itf&gt;</code> is the name of its Runnable interface, if it 
 * has any.
 * <p>By default the component is run without static checking of the protocols.
 * To check the component (and not run) use the <code>-check</code> option.
 * <p>To check the component's implementation with JPF use the <code>-checkjpf</code> option. 
 * In that case, &lt;tmp directory&gt; is the path to a directory where temporary files for the component's environment are stored.
 */

public class Launcher {

  /**
   * Container for to parsed parameters of the launcher.
   */
  private static class LauncherParams {
    /** 
     * The component will be checked instead of run.
     */
    public boolean doCheck;

    /** 
     * Component's implementation will be checked by JPF.
     */
    public boolean doCheckJPF;
    
    /**
     * Path to the directory where temporary files for environment are stored.
     * Used only when checking the code with JPF.
     */
    public String tmpDir;
    
    /**
     * Name of the component definition to be instantiated.
     */
    public String component;

    /**
     * Name of the runnable interface of the component to be instantiated.
     */
    public String runIface;
  }
  
  private Launcher () {
  }

  /**
   * Parses the arguments as described in the class description and instantiates a component accordingly.
   *
   * @param args Command-line parameters.
   */
  public static void main (final String[] args) throws Exception {
    LauncherParams pargs = parseArgs(args);
    Component comp = createComponent(pargs);

    if (pargs.doCheck) {
      System.out.print("Checking for compliance ...");
      NestedCheckingResult res = ProtocolChecker.check(comp);
      
      if (res.getErrorType() != NestedCheckingResult.ERR_OK) {
        System.out.println(" Error:");
        System.out.println(res.toString());
      } else {
        System.out.println("The behavior checking is finished. The behavior is OK");
      }
    } 
    else if (pargs.doCheckJPF)
    {
        System.out.println("Checking implementation of primitive components with JPF ...");
        
        boolean res = JPFChecker.check(comp, pargs.tmpDir);
    }
    else 
    {
      LifeCycleController lc = null;
      try {
        lc = Fractal.getLifeCycleController((Component)comp);
      } catch (NoSuchInterfaceException ignored) {
      }
  
      if (lc != null) {
        lc.startFc();
      }
  
      
      Runnable r = null;
      try {
        r = (Runnable)((Component)comp).getFcInterface(pargs.runIface);
      } catch (NoSuchInterfaceException ignored) {
      }
  
      if (r != null) {
        r.run();
      }
      
      if (lc != null) {
      	lc.stopFc();
      }
    }
  }

  /**
   * Instantiates a component.
   * Instantiates a Fractal ADL parser able to handle protocol extensions and uses it to instantiate a component described by the extended ADL.
   *
   * @param pargs Parsed command-line parameters.
   * @return Created component.
   */
  private static Component createComponent (LauncherParams pargs) throws Exception {
    Factory f = FactoryFactory.getFactory("org.objectweb.fractal.bpc.adl.Factory","org.objectweb.fractal.bpc.adl.FractalBackend", new HashMap());
    return (Component)f.newComponent(pargs.component, new HashMap());
  }
  
  /**
   * Parses command-line parameters.

   * @param args Command-line parameters.
   * @return Command-line parameters wrapped to a structure.
   */
  private static LauncherParams parseArgs (final String[] args) {
    
    LauncherParams pargs = new LauncherParams();
    
    if (args.length < 1 || args.length > 4) 
    {
      parseError();
    }
    
    int paramShift = 0;
    if (args[0].equals("-check")) 
    {
      pargs.doCheck = true;
      pargs.doCheckJPF = false;
      paramShift++;
      
      if (args.length < 2) 
      {
        parseError();
      }
    }
    else if (args[0].equals("-checkjpf")) 
    {
      pargs.doCheck = false;
      pargs.doCheckJPF = true;
      paramShift++;
      
      if (args.length < 3) 
      {
        parseError();
      }
      
      pargs.tmpDir = args[paramShift];
      paramShift++;
    }
    else 
    {
      pargs.doCheck = false;
      pargs.doCheckJPF = false;
    }
    
    pargs.component = args[paramShift];
    pargs.runIface = args.length-paramShift >= 2 ? args[paramShift+1] : "run";
    
    return pargs;
  }
  
  /**
   * Prints out help to the launcher usage.
   */
  private static void parseError () {
    System.out.println("Usage: Launcher [-check | -checkjpf <tmp directory>] <definition> [ <itf> ]");
    System.out.println("where <definition> is the name of the component to be instantiated and");
    System.out.println("started, and <itf> is the name of its Runnable interface, if it has any.");
    System.out.println("By default the component is run without static checking of the protocols.");
    System.out.println("To check the component (and not run) use the -check option.");
    System.out.println("To check the component's implementation with JPF use the -checkjpf option. In that case, <tmp directory> is the path to a directory where temporary files for the component's environment are stored.");
    System.exit(1);
  }
}
